/* Copyright 2014 Jan Wolter */

#include "stack.h"
#include "tableau.h"
#include "found.h"
#include "course.h"
#include "stock.h"
#include "solve.h"
#include "game.h"

#include <unistd.h>

char *gamename= "Shlomi Fish's Black Hole Solver";
char *gameopt= "IPR";
Stack *gathered= NULL;
Stack *temp= NULL;

int nplayed= 0;
int nwon= 0;
int nabandoned= 0;
int cardsleft= 0;
int maxoff;
int npruned;

int invert= 0;
int byrow= 0;
int prune= 0;
int allinarow= 0;

void setopt(char c)
{
    switch (c)
    {
	case 'A': allinarow= 1; break;	/* Play all in a row */
	case 'I': invert= 1; break;	/* Invert the game - with -A only */
	case 'R': byrow= 1; break;	/* Deal by row instead of col */
	case 'P': prune= 1; break;	/* Prune some moves leading to
					   unwinnable states */
    }
}

/* black-hole-solve --game {all_in_a_row|black_hole} [more options] 
 *  [/path/to/board_layout.txt]
 *
 * --help                        displays this help.
 * --max-iters [iter_count]      limit the iterations.
 * --game all_in_a_row           solve All in a Row games.
 * --game black_hole             solve Black Hole games.
 * --displays-boards             display the layout of the board at every step.
 * --rank-reach-prune            enable the Rank Reachability Prune.
 * --iters-display-step [step]   Display a trace every certain step.
 */

#define FISHPATH "/home/jan/src/solsolve/fish/black-hole-solver-0.12.0/build/black-hole-solve"
#define FISHNAME "black-hole-solve"


static FILE *fishin= NULL;	/* pipe to Child */
static FILE *fishout= NULL;	/* Pipe from child */
static pid_t fish_pid;		/* Process ID of child */

int execfish(char **arg)
{
    int pipe_in[2];
    int pipe_out[2];

    if (pipe(pipe_in) || pipe(pipe_out))
    	return 0;

    switch (fish_pid= fork())
    {
    case 0: /* Child - do the exec */

	/* Close parent's ends of pipes */
	close(pipe_in[1]);
	close(pipe_out[0]);
	/* Attach pipe_in[0] to child's stdin */
	if (pipe_in[0] != 0)
	{
	    dup2(pipe_in[0],0);
	    close(pipe_in[0]);
	}
	/* Attach pipe_out[1] to child's stdout */
	if (pipe_out[1] != 1)
	{
	    dup2(pipe_out[1],1);
	    close(pipe_out[1]);
	}

	/* Run Shlomi's Program */
	execv(FISHPATH, arg);
	exit(127);

    case -1: /* Parent - Fork failed */
	close(pipe_in[0]);
	close(pipe_in[1]);
	close(pipe_out[0]);
	close(pipe_out[1]);
    	return 0;

    default: /* Parent - Fork succeeded */

	/* Close child's ends of the pipes */
    	close(pipe_in[0]);
    	close(pipe_out[1]);

	/* Start buffered I/O on useful ends of pipes */
	fishin= fdopen(pipe_in[1],"w");
	fishout= fdopen(pipe_out[0],"r");
	return 1;
    }
}


/* This is the main program for setting up the current game */
void exec(int gather, int arif, int over, int cut)
{
    if (gather)
    {
	printf("Cannot Gather\n");
	exit(1);
    }

    /* Build and shuffle the deck */
    Stack *shuf= mixcards(arif, over, cut);

    /* Create the waste/foundation thingy */
    makeCourse(1,1,NULL);

    /* Put an Ace on the foundation (unless we are inverted) */
    if (!allinarow)
    {
	Card *c= extractCard(shuf,1,0);
	if (c != NULL)
	{
	    c->faceup= 1;
	    pushStack(stacks+course[0].id, c);
	}
    }

    int ncol= allinarow ? 13 : 17;
    int nrow= allinarow ? 4 : 3;

    /* Create the tableau */
    for (int i= 1; i <= ncol; i++)
	makeTableau(byrow ? 0 : nrow, FLIP_NONE, shuf,
	    S_NONE, 0, S_NONE, 0, 1, F_NONE, BREAK_NOWHERE, 0);

    if (byrow)
    {
	for (int j= 0; j < nrow; j++)
	    for (int i= 0; i < ncol; i++)
	    {
		Card *c= popStack(shuf);
		if (c == NULL) break;
		pushStack(stacks+tableau[i].id, c);
	    }
    }

    if (invert && allinarow)
    {
	for (int i= 0; i < ncol; i++)
	{
	    Stack *s= stacks + tableau[i].id;

	    Card *c= s->c[0]; s->c[0]= s->c[3]; s->c[3]= c;
	    c= s->c[1]; s->c[1]= s->c[2]; s->c[2]= c;
	}
    }

    freeStack(shuf);

    /* Construct command line arguments */
    char *arg[25];
    arg[0]= FISHNAME;
    arg[1]= "--game";
    arg[2]= allinarow ? "all_in_a_row" : "black_hole";
    int i= 3;
    if (prune) arg[i++]= "--rank-reach-prune";
    arg[i++]= "-";  /* Read from stdin */
    arg[i]= NULL;

    if (verbose > 1) printState(stdout, stacks);

    /* Run child program */
    if (!execfish(arg))
    {
	printf("Exec of Child Program Failed.");
	exit(1);
    }

    /* Write game layout to child */
    printStack(fishin, "Foundations", stacks+course[0].id);
    for (int k= 0; k < ntableau; k++)
	printStack(fishin, NULL, stacks+tableau[k].id);
    fclose(fishin);

    if (verbose > 4)
    {
	printStack(stdout, "----------\nFoundations", stacks+course[0].id);
	for (int k= 0; k < ntableau; k++)
	    printStack(stdout, NULL, stacks+tableau[k].id);
	printf("----------\n");
    }

    nplayed++;
    char bf[1024], *result, moves[520];
    int line= 0, nm= 0, check= -1, gen= -1;
    moves[0]= '\0';
    while (fgets(bf, 1024, fishout) != NULL)
    {
	line++;
	if (line == 1)
	{
	    if (strcmp(bf,"Solved!\n") == 0)
	    {
		result= "Victory!";
		nwon++;
	    }
	    else if (strcmp(bf,"Unsolved!\n") == 0)
		result= "Stuck!";
	    else if (strcmp(bf,"Unsolved!\n") == 0)
	    {
		result= "Abandoned!";
		nabandoned++;
	    }
	    else
		result= "Weird?";
	}
	else if (verbose > 1 && strncmp(bf,"Info: Card moved is ",20) == 0)
	{
	    if (nm > 78) {strcat(moves,"\n"); nm= 0; }
	    if (nm > 0) {strcat(moves," "); nm++; }
	    strncat(moves, bf+20, 2); nm+= 2;
	}
	else if (strncmp(bf,"Total number of states checked is ",34) == 0)
	    check= atoi(bf+34);
	else if (strncmp(bf,"This scan generated ",20) == 0)
	    gen= atoi(bf+20);

	if (verbose > 3)
	    fputs(bf, stdout);
    }
    fclose(fishout);
    printf("%s checked=%d generated=%d\n", result, check, gen);
    if (verbose > 1) puts(moves);

    cleanFound();
    cleanTableau();
    cleanCourse();
    cleanStacks();
}

void gatherAll() {}
int rateMove(Stack *src, int index1, int len, Stack *dst) { return -1;}
int victory() {return 0;}
int quality() { return 0; }

void printState(FILE *f, Stack *stks)
{
    printTableau(f, stks);
    printCourse(f, stks);
}

void summary()
{
    printf("Played:    %d games\n",nplayed);
    printf("Won:       %d games (%.2f%)\n",nwon,(float)nwon*100/nplayed);
    printf("Abandoned: %d games (%.2f%)\n",nabandoned,(float)nabandoned*100/nplayed);
}

char *currstate() { return NULL;}
